package tfnode

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"strings"

	"encoding/json"
	"runtime"
	"sync/atomic"
	"time"
)

//server runtime stats
type Stats struct {
	Start                                                          time.Time
	ServerId                                                       int
	State                                                          int32
	FreeSpace                                                      int64
	CurrentConnections, TotalConnections                           int64
	BytesRead, BytesWritten, FilesDeleted, FilesRead, FilesWritten int64
	TotalReadTime, TotalWriteTime                                  int64
	DiskReadErrors, DiskWriteErrors                                int64
	ReplGroupSplitErrors                                           int64
	BadArgErrors                                                   int64
	BytesAlloc, ObjectsAlloc                                       uint64
}

func NewStats(srvId int) *Stats {
	s := new(Stats)
	s.Start = time.Now()
	s.ServerId = srvId
	return s
}

func (s *Stats) ProfileMemory() {
	m := new(runtime.MemStats)
	runtime.ReadMemStats(m)
	s.BytesAlloc = m.TotalAlloc
	s.ObjectsAlloc = m.HeapObjects
}

func (s *Stats) AddConnection() {
	atomic.AddInt64(&s.CurrentConnections, 1)
	atomic.AddInt64(&s.TotalConnections, 1)
}

func (s *Stats) RemoveConnection() {
	atomic.AddInt64(&s.CurrentConnections, -1)
}

func (s *Stats) OnRead(bytes int64, ms int64) {
	atomic.AddInt64(&s.BytesRead, bytes)
	atomic.AddInt64(&s.TotalReadTime, ms)
	atomic.AddInt64(&s.FilesRead, 1)
}

func (s *Stats) OnWrite(bytes int64, ms int64) {
	atomic.AddInt64(&s.BytesWritten, bytes)
	atomic.AddInt64(&s.TotalWriteTime, ms)
	atomic.AddInt64(&s.FilesWritten, 1)
}

func (s *Stats) OnDelete() {
	atomic.AddInt64(&s.FilesDeleted, 1)
}

func (s *Stats) OnBadArgError() {
	atomic.AddInt64(&s.BadArgErrors, 1)
}

func (s *Stats) OnDiskReadError() {
	atomic.AddInt64(&s.DiskReadErrors, 1)
}

func (s *Stats) OnDiskWriteError() {
	atomic.AddInt64(&s.DiskWriteErrors, 1)
}

func (s *Stats) DiskErrors() int64 {
	return atomic.LoadInt64(&s.DiskReadErrors) + atomic.LoadInt64(&s.DiskWriteErrors)
}

func (s *Stats) OnReplGroupSplitError() {
	atomic.AddInt64(&s.ReplGroupSplitErrors, 1)
}

func (s *Stats) ToJson() ([]byte, error) {
	return json.Marshal(s)
}

//report stats to the monitor
func (s *Server) ReportStats() bool {
	s.stats.ProfileMemory()

	//add server state
	s.stats.State = s.getState()

	//add free storage space
	s.stats.FreeSpace = s.store.StatFreeSpace()

	data, err := s.stats.ToJson()
	if err != nil {
		s.fatalLog.Println("Fatal: get the stats information error", err)
	}

	buff := bytes.NewBuffer(data)
	resp, err := http.Post("http://"+s.monitor, "application/json", buff)
	if err != nil {
		s.fatalLog.Println("Fatal: monitor get the response error", err)
		return false
	}
	data, _ = ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if strings.Contains(string(data), `{"Success":"true"}`) {
		s.workLog.Println("Info: monitor data send success")
		return true
	}
	s.errLog.Println("Error: monitor data send failed")
	return false
}
